package persistence.dao;

import java.sql.Connection;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;
import javax.naming.InitialContext;
import javax.sql.DataSource;
import org.apache.log4j.Logger;
import persistence.dao.base.ConnectionBehavior;
import persistence.dao.base.ConnectionProvider;
import persistence.dao.base.DaoCommand;
import persistence.dao.impl.CityDaoImpl;
import persistence.dao.impl.OrderDaoImpl;
import persistence.dao.impl.RoutsDaoImpl;
import persistence.dao.impl.RoutsStantionDaoImpl;
import persistence.dao.impl.UsersDaoImpl;

public class DaoManager {
	
	private static final String JNDI_DB_REF = "java:/comp/env/jdbc/appDb";
	private static final Logger logger = Logger.getLogger(DaoManager.class);
	private static final String DAOMANAGER_INFO = "DaoManager is error Connection"; 
	private static final String DAOMANAGER_TRANSACTION = "DaoManager is error transaction";
	private static final String DAOMANAGER_FINALLY = "DaoManager is error in FINALLY";
	private static final String DAOMANAGER_COMMAND = "DaoManager is error command";

	@SuppressWarnings("rawtypes")
	private Map<Class, Object> daoObjects;
	private Connection connection;
	private ConnectionProvider provider;

	@SuppressWarnings("rawtypes")
	private DaoManager(ConnectionProvider provider) {
		super();
		this.provider = provider;
		this.daoObjects = new HashMap<Class, Object>();
	}
	
	private void refreshState() {
		try {
			if (getConnection() == null || getConnection().isClosed()) {
				
				this.connection = this.provider.getConnection();
				daoObjects.put(RoutsDao.class, new RoutsDaoImpl(getConnection()));
				daoObjects.put(CityDao.class, new CityDaoImpl(getConnection()));
				daoObjects.put(UsersDao.class, new UsersDaoImpl(getConnection()));
				daoObjects.put(OrderDao.class, new OrderDaoImpl(getConnection()));
				daoObjects.put(RoutsStantionDao.class, new RoutsStantionDaoImpl(getConnection()));
			}
		} catch (SQLException e) {
			this.connection = provider.getConnection();
		}
	}
	
	private Connection getConnection()
	{
		return this.connection;
	}

	public static DaoManager getDefault() {
		return new DaoManager(new ConnectionProvider() {
			@Override
			public Connection getConnection() {
				try {
					DataSource dataSource = 
							(DataSource) new InitialContext().lookup(DaoManager.JNDI_DB_REF);
					return dataSource.getConnection();
				} catch (Exception e) {
					logger.error(DaoManager.DAOMANAGER_INFO + e.getMessage());
					return null;
				}
				
				
			}
		});
	}
	
	public Object transaction(DaoCommand command, ConnectionBehavior behavior) {
		try {
				this.refreshState();
				getConnection().setAutoCommit(false);
				Object returnValue = command.execute(this);
				getConnection().commit();
				return returnValue;
			} 
			catch(Exception e) {
				try {
					getConnection().rollback();
				} 
				catch (SQLException e1) {
					logger.error(DaoManager.DAOMANAGER_TRANSACTION + e1.getMessage());
				}
			logger.error(DaoManager.DAOMANAGER_TRANSACTION + e.getMessage());
			}
			finally {
				try {
					getConnection().setAutoCommit(true);
					if (ConnectionBehavior.CLOSE.equals(behavior)) {
						getConnection().close();
					}
				} 
				catch (SQLException e) {
					logger.error(DaoManager.DAOMANAGER_FINALLY + e.getMessage());
				}
			}
		return behavior;
	}
	
	public Object command(DaoCommand command, ConnectionBehavior behavior) {
		
		this.refreshState();
		Object result = command.execute(this);

		if (ConnectionBehavior.CLOSE.equals(behavior)) {
			try {
				getConnection().close();
			} catch (SQLException e) {
				logger.error(DaoManager.DAOMANAGER_COMMAND + e.getMessage());
			}
		}
		return result;
	}

	public <T> T getDao(Class<?> daoInterface) {
		@SuppressWarnings("unchecked")
		final T dao = (T) daoObjects.get(daoInterface);
		if (dao == null) {
			throw new IllegalArgumentException("Unsupported DAO type:" + daoInterface.getClass().getName());
		}
		return dao;
	}
}
